import { mouse } from '@/utils/keyboardAndMouse'
import { randomStr } from '@/utils/random'
import { isEqual } from 'lodash-es'
import { onMounted, onUnmounted, TemplateRef, unref } from 'vue'

export interface IUseSelectableOptions {
    /** 容器 */
    container?: HTMLElement | TemplateRef<HTMLElement>
    /** 可选择的元素，css选择器 */
    selectableSelector?: string
    /** 当被选中的元素发生变化时触发 */
    onSelectedChange?: (selected: HTMLElement[]) => void
    /** 是否为容器注册mousedown事件，
     * 为true时，将自动注册mousedown事件，
     * false时需要手动将容器的mousedown事件添加selectStart函数 */
    registerContainerMousedown?: boolean
}

// 默认参数
const defaultOptions: IUseSelectableOptions = {
    container: document.body,
    selectableSelector: undefined,
    onSelectedChange: () => {},
    registerContainerMousedown: true
}

// 设置样式
const setStyle = (dom: HTMLElement, style: any) => {
    Object.assign(dom.style, style)
}

// 判断一个点是否在一个矩形内
// const isInRect = (point: { x: number; y: number }, rect: DOMRect) => {
//     return (
//         point.x >= rect.left &&
//         point.x <= rect.right &&
//         point.y >= rect.top &&
//         point.y <= rect.bottom
//     )
// }

// 根据rect判断是否交叠
const isIntersect = (rect1: DOMRect, rect2: DOMRect) => {
    // console.log(rect1)
    // console.log(rect2)
    return !(
        rect1.right < rect2.left ||
        rect1.left > rect2.right ||
        rect1.bottom < rect2.top ||
        rect1.top > rect2.bottom
    )
}

export const useSelectable = (options: IUseSelectableOptions = defaultOptions) => {
    // 合并参数
    options = { ...defaultOptions, ...options }
    // 框选框
    const selectBox = document.createElement('div')
    selectBox.id = 'selectBox' + randomStr()
    selectBox.style.pointerEvents = 'none'
    selectBox.className = 'select-box absolute bg-blue-300 opacity-50 border border-solid border-blue-500 left-0 top-0'
    document.body.appendChild(selectBox)

    // 开始位置
    let startX = 0
    let startY = 0
    // 是否正在框选
    let isSelecting = false
    // 待框选的元素及boundingClientRect
    let selectableElements: { el: HTMLElement; rect: DOMRect }[] = []
    // 被选中的元素
    let selectedElements: HTMLElement[] = []
    const setSelectedElements = (elements: HTMLElement[]) => {
        selectedElements = elements
        options.onSelectedChange?.(selectedElements)
    }

    // 容器默认position样式
    let _containerDefaultStylePosition = ''
    // 把selectBox添加到容器中
    const showSelectBox = () => {
        const container = unref(options.container)
        if (!container) return
        container.appendChild(selectBox)
        setStyle(selectBox, { display: 'block' })
        _containerDefaultStylePosition = container.style.position
        if (_containerDefaultStylePosition === '' || _containerDefaultStylePosition === 'static') {
            container.style.position = 'relative'
        }
    }
    // 隐藏selectBox
    const hideSelectBox = () => {
        const container = unref(options.container)
        if (!container) return
        container.removeChild(selectBox)
        setStyle(selectBox, { display: 'none' })
        if (_containerDefaultStylePosition !== container.style.position) {
            container.style.position = _containerDefaultStylePosition
        }
    }

    // 检测并选中
    const checkSelect = () => {
        const selectBoxRect = selectBox.getBoundingClientRect()
        const selected = selectableElements.filter((v) => isIntersect(selectBoxRect, v.rect)).map((v) => v.el)
        if (!isEqual(selected, selectedElements)) {
            setSelectedElements(selected)
        }
    }

    // 框选中，mousemove事件
    const selectMove = (e: MouseEvent) => {
        if (!isSelecting) return
        const { layerX: x, layerY: y } = e
        const offsetX = x - startX
        const offsetY = y - startY
        const info = {
            x: Math.min(x, startX),
            y: Math.min(y, startY),
            width: Math.abs(offsetX),
            height: Math.abs(offsetY)
        }
        setStyle(selectBox, {
            left: info.x + 'px',
            top: info.y + 'px',
            width: info.width + 'px',
            height: info.height + 'px'
        })
        checkSelect()
    }

    // 框选结束，mouseup事件
    const selectEnd = (_e: MouseEvent) => {
        hideSelectBox()
        if (!isSelecting) return
        isSelecting = false
        // document.removeEventListener('mousemove', selectMove)
        // document.removeEventListener('mouseup', selectEnd)
        mouse.off('mousemove', selectMove)
        mouse.off('mouseup', selectEnd)
    }

    // 框选开始，mousedown事件
    const selectStart = (e: MouseEvent) => {
        showSelectBox()
        const { layerX: x, layerY: y } = e
        startX = x
        startY = y
        isSelecting = true
        setStyle(selectBox, {
            left: x + 'px',
            top: y + 'px',
            width: '0px',
            height: '0px'
        })

        // 添加鼠标移动事件
        mouse.on('mousemove', selectMove)
        mouse.on('mouseup', selectEnd)
        // document.addEventListener('mousemove', selectMove)
        // document.addEventListener('mouseup', selectEnd)

        // 根据选择器选中所有可被框选的元素，并拿到矩形
        const container = unref(options.container)
        const { selectableSelector } = options
        // 重新赋值
        selectableElements = []
        setSelectedElements([])

        if (!container || !selectableSelector) return
        container.querySelectorAll<HTMLElement>(selectableSelector).forEach((el) => {
            selectableElements.push({
                el,
                rect: el.getBoundingClientRect()
            })
        })
        // 检测一次框选
        checkSelect()
    }

    // 添加鼠标按下事件监听
    onMounted(() => {
        if (!options.registerContainerMousedown) return
        const container = unref(options.container)
        if (!container) return
        container.addEventListener('mousedown', selectStart)
    })

    onUnmounted(() => {
        // document.removeEventListener('mousemove', selectMove)
        // document.removeEventListener('mouseup', selectEnd)
        mouse.off('mousemove', selectMove)
        mouse.off('mouseup', selectEnd)

        if (!options.registerContainerMousedown) return
        const container = unref(options.container)
        if (!container) return
        container.removeEventListener('mousedown', selectStart)
    })

    return { selectStart }
}
